package cn.twh.wall.config;

import cn.twh.wall.util.OgnlUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import ognl.OgnlException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

@RefreshScope
@Component
@Configuration
@ConfigurationProperties(prefix = "wall")
@Data
@Slf4j
public class WallConfig implements InitializingBean {

    /**
     * 默认失败率 0-100
     */
    private int failRate;

    /**
     * 默认超时率 0-100
     */
    private int timeOutRate;

    /**
     * 接口失败率 样例： interfaceFailRate: api: 50
     */
    private Map<String, Integer> interfaceFailRate = new HashMap<>();

    /**
     * 接口失败率 样例： interfaceTimeOutRate: api: 50
     */
    private Map<String, Integer> interfaceTimeOutRate = new HashMap<>();

    /**
     * 接口失败标志 样例： interfaceFailFlag: api: ognl-key1: - val1 - val2
     */
    private Map<String, Map<String, Set<String>>> interfaceFailFlag = new HashMap<>();

    /**
     * 接口超时标志 样例： interfaceTimeOutFlag: api: ognl-key1: - val1 - val2
     */
    private Map<String, Map<String, Set<String>>> interfaceTimeOutFlag = new HashMap<>();
    /**
     * 压测模式：保留，暂未使用 true 会去掉大部分耗时操作并使用缓存，如果在写挡板逻辑请设定为false. false 默认false
     */
    private boolean pressureMode = false;

    private Map<String, String> uriToApi;

    /**
     * 获取api的失败权重
     *
     * @param api 请求接口
     * @return 接口失败率 当接口特别指定失败率不存在时使用默认失败率
     */
    public int getFailedRate(String api) {
        if (interfaceFailRate.containsKey(api)) {
            return interfaceFailRate.get(api);
        } else {
            return failRate;
        }
    }

    /**
     * 获取api的超时权重
     *
     * @param api 请求接口
     * @return 接口超时率 当接口特别指定超时率不存在时使用默认超时率
     */
    public int getTimeOutRate(String api) {
        if (interfaceTimeOutRate.containsKey(api)) {
            return interfaceTimeOutRate.get(api);
        } else {
            return timeOutRate;
        }
    }

    /**
     * 是否包含失败标志
     *
     * @param data 请求报文
     * @param api  接口
     * @return 是否包含 true 包含 false 不包含
     */
    public boolean containsFailedFlag(Map<?, ?> data, String api) {
        return containsFlag(data, api, interfaceFailFlag);
    }

    /**
     * 是否包含超时标志
     *
     * @param data 请求报文
     * @param api  接口
     * @return 是否包含 true 包含 false 不包含
     */
    public boolean containsTimeOutFlag(Map<?, ?> data, String api) {
        return containsFlag(data, api, interfaceTimeOutFlag);
    }

    /**
     * 判断是否包含flag
     *
     * @param data 请求报文
     * @param api  接口
     * @return 是否包含 true 包含 false 不包含
     */
    private boolean containsFlag(Map<?, ?> data, String api,
                                 Map<String, Map<String, Set<String>>> interfaceFlag) {
        if (interfaceFlag.containsKey(api)) {
            Map<String, Set<String>> flags = interfaceFlag.get(api);
            for (Entry<String, Set<String>> item : flags.entrySet()) {
                try {
                    String val = OgnlUtil.getValue(data, item.getKey());
                    if (item.getValue().contains(val)) {
                        // 超时响应匹配
                        return true;
                    }
                } catch (OgnlException e) {
                    log.error("application properties interfaceTimeOutFlag ognl illegal：" + item.getKey(), e);
                    return false;
                }
            }
        }
        return false;
    }

    @Override
    public void afterPropertiesSet() {
        Object[] e = uriToApi.keySet().toArray();
        for (Object key : e) {
            uriToApi.put(uriToApi.get(key), key.toString());
            uriToApi.remove(key);
        }
    }
}
